﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace iMario
{
    public class Map : GameObject
    {
        //Mục đích class Map
        //  - Chứa các thuộc tính và phương thức xử lí bản đồ
        

        //Danh sách các thuộc tính và ý nghĩa
        static public String ClassName = "Map"; //Tên lớp đối tượng
        static public int MapWidth = 200;       //Chiều dài của ma trận bản đồ
        static public int MapHeight = 20;       //Chiều cao của ma trận bản đồ
        static public int WindowWidth = 800;    //Chiều dài màn hình game
        static public int WindowHeigh = 600;    //Chiều cao màn hình game
        static public int TileSize = 32;        //Kích thước (width & height) của 1 tile background
        static public int MapMovement = 4;      //Giá trị cộng thêm vào hoặc trừ đi ở biến mapBegin.X khi thực hiện Update

        private List<List<int>> _mapMatrix;     //Ma trận 2 chiều biểu diễn bản đồ
        public List<List<int>> mapMatrix
        {
            get { return _mapMatrix; }
        }

        private Texture2D _backGround;          //Texture sử dụng làm background cho đối tượng bản đồ
        public Texture2D backGround
        {
            get { return _backGround; }
        }

        private Texture2D _tileArray;           //Texture chứa các tile dùng để vẽ background
        public Texture2D tileArray
        {
            get { return _tileArray; }
        }

        private int _nTile;                     //Số tile trong Texture
        public int nTile
        {
            get { return _nTile; }
        }

        private Point _mapBegin;                //Vị trí góc trái dưới của bản đồ sẽ được load trùng với góc trái dưới của màn hình game
        public Point mapBegin
        {
            get { return _mapBegin; }
        }

        private bool _isLoadMapSucceed;         //Cờ hiệu cho biết đã đọc bản đồ từ file thành công hay không
                                                //  - True: Đọc bản đồ thành công       - False: Đọc bản đồ thất bại
        

        //Danh sách các phương thức và ý nghĩa
        //Hàm dựng mặc định không có tham số
        //  1. Tham số truyền vào: Không có
        //  2. Giá trị trả về: Không có
        public Map()
        { 
            //Hướng xử lí
            //  1. Thiết lập giá trị ban đầu cho các thuộc tính


            //Tiến hành
            //  1. Thiết lập giá trị ban đầu cho các thuộc tính
            _mapBegin = new Point(0, 0);

            _isLoadMapSucceed = false;

            _mapBegin = new Point(0, -40);
        }

        //Hàm dựng truyền tham số là đường dẫn của file bản đồ
        //  1. Tham số truyền vào:
        //      - String _path: Đường dẫn cho file .txt chứa bản đồ
        //  2. Giá trị trả về: Không có
        public Map(String _path)
        {
            //Hướng xử lí trong hàm dựng Map với tham số truyền vào là chuỗi đường dẫn đến file chứa bản đồ
            //  1. Khởi tạo các thuộc tính
            //  2. Gọi hàm LoadMap và lưu giá trị trả về vào biến _isLoadMapSucceed


            //Tiến hành
            //  1. Khởi tạo các thuộc tính
            _mapBegin = new Point(0, 0);
            
            _mapMatrix = new List<List<int>>();
            for (int i = 0; i < MapHeight; i++)
                _mapMatrix.Add(new List<int>());

            for (int i = 0; i < MapHeight; i++)
                for (int j = 0; j < MapWidth; j++)
                    _mapMatrix[i].Add(new int());

            _isLoadMapSucceed = false;

            _mapBegin = new Point(0, -40);

            //  2. Gọi hàm LoadMap và lưu giá trị trả về vào biến _isLoadMapSucceed
            _isLoadMapSucceed = LoadMap(_path);
        }

        //Hàm nhân bản thêm 1 bản đồ mới và trả về bản đồ đã được nhân bản thành công đó
        //  1. Tham số truyền vào: Không có
        //  2. Giá trị trả về:
        //      - Con trỏ GameEntity của đối tượng đã được nhân bản thành công
        public override GameEntity Clone()
        {
            //Hướng xử lí
            //  1. Khởi tạo 1 con trỏ GameEntity
            //  2. Gán toàn bộ các giá trị của đối tượng hiện tại (this) cho con trỏ vừa được khởi tạo
            //  3. Trả về chính con trỏ này => kết thúc hàm

            //Tiến hành
            //  1. Khởi tạo 1 con trỏ GameEntity
            GameObject _map = new Map();

            //  2. Gán toàn bộ các giá trị của đối tượng hiện tại (this) cho con trỏ vừa được khởi tạo
            ((Map)_map).SetBackGround(this.backGround);
            ((Map)_map).SetTileBackGround(this.tileArray);
            ((Map)_map)._mapMatrix = this.mapMatrix;
            ((Map)_map)._isLoadMapSucceed = true;

            //  3. Trả về chính con trỏ này => kết thúc hàm
            return (GameObject)_map;
        }

        //Hàm cập nhật trạng thái của bản đồ
        //  1. Tham số truyền vào:
        //      - KeyboardState _keyboard: biểu diễn trạng thái của bàn phím
        //      - MouseState _mouse: biểu diễn trạng thái chuột
        //      - GameTime _gameTime
        //  2. Giá trị trả về: Không có
        public override void Update(KeyboardState _keyboard, MouseState _mouse, GameTime _gameTime)
        {
            //Hướng xử lí
            //  Xét từng trường hợp của bản phím thông qua biến _keyboard
            //  1. Nếu IsKeyDown(Keys.Left) == true
            //      Dịch chuyển bản đồ sang phải <=> mapBegin.X -= _mapMovement || mapBegin.X = 0
            //  2. Nếu IsKeyDown(Keys.Right) == true
            //      Dịch chuyển bản đồ sang trái <=> mapBegin.X += _mapMovement || mapBegin.X =  (299 - WindowWidth / TileSize - 1) * TileSize


            //Tiến hành
            //  Xét từng trường hợp của bản phím thông qua biến _keyboard
            //  1. Nếu IsKeyDown(Keys.Left) == true
            if (_keyboard.IsKeyDown(Keys.Left))
            {
                //      Dịch chuyển bản đồ sang phải <=> mapBegin.X -= _mapMovement || mapBegin = 0
                _mapBegin.X -= MapMovement;
                if (_mapBegin.X < 0)
                    _mapBegin.X = 0;
            }

            //  2. Nếu IsKeyDown(Keys.Right) == true
            if (_keyboard.IsKeyDown(Keys.Right))
            {
                //      Dịch chuyển bản đồ sang trái <=> mapBegin.X += _mapMovement || mapBegin.X =  (299 - WindowWidth / TileSize - 1) * TileSize
                _mapBegin.X += MapMovement;
                if (_mapBegin.X > ((MapWidth - 1) - (WindowWidth / TileSize) - 1) * TileSize)
                    _mapBegin.X = ((MapWidth - 1) - (WindowWidth / TileSize) - 1) * TileSize;
            }
            
        }

        //Hàm đọc dữ liệu từ file bản đồ và thể hiện bản đồ lên ma trận _mapMatrix
        //  1. Tham số truyền vào:
        //      - Đường dẫn đến file bản đồ cần đọc: String _path
        //  2. Giá trị trả về:
        //      - True: Đọc file thành công
        //      - False: Đọc file thất bại và có lỗi phát sinh
        private bool LoadMap(String _path)
        {
            //Hướng xử lí trong hàm LoadMap
            //  1. Khai báo biến String _stringTemp dùng để đọc toàn bộ chuỗi chứa trong file bản đồ
            //  2. Khai báo biến List<int> _row dùng để chứa thông tin 1 hàng của ma trận bản đồ sau khi tách từ chuỗi đọc từ file
            //  3. Khai báo biến _streamReader thuộc System.Io.StreamReader để đọc file có đường dẫn là _path
            //  4. Đọc file bằng cú pháp try...catch...finnaly
            //  5. Nếu đọc thành công, tách chuỗi _temp thành ma trận địa hình và lưu vào _mapMatrix
            //  6. Đóng file và kết thúc hàm


            //Tiến hành
            //  1. Khai báo 1 List<int> _temp dùng để đọc toàn bộ ma trận chứa trong file bản đồ
            System.IO.StreamReader _streamReader = new System.IO.StreamReader(@"../../../" + _path);

            //  2. Khai báo biến List<int> _row dùng để chứa thông tin 1 hàng của ma trận bản đồ sau khi tách từ chuỗi đọc từ file
            List<int> _row = new List<int>();

            //  3. Khai báo biến _streamReader thuộc System.Io.StreamReader để đọc file có đường dẫn là _path
            String _stringTemp;

            //  4. Đọc file bằng cú pháp try...catch...finnaly
            try
            {
                //  5. Đọc thành công, tách chuỗi _temp thành ma trận địa hình và lưu vào _mapMatrix
                _stringTemp = _streamReader.ReadToEnd();
                for (int i = 0; i < _stringTemp.Length; i++)
                    if (i % 4 == 0)
                        _row.Add(_stringTemp[i]);

                //Tách thành ma trận địa hình
                for (int i = 0; i < _row.Count; i++)
                    _mapMatrix[i / MapWidth][i % MapWidth] = _row[i];
            }
            catch (Exception ex)
            {
                return false;
            }
            finally
            {
                //  6. Đóng file và kết thúc hàm
                _streamReader.Close();
            }

            return true;
        }

        //Hàm thiết lập Texture2D được dùng làm background cho đối tượng
        //  1. Tham số truyền vào:
        //      - Texture2D _backGroundTexture
        //  2. Giá trị trả về: Không có
        public void SetBackGround(Texture2D _backGroundTexture)
        {
            _backGround = _backGroundTexture;
        }

        //Hàm thiết lập Texture2D chứa danh sách các tile dùng trong việc vẽ bản đồ lên màn hình game
        //  1. Tham số truyền vào:
        //      - Texture2D _tileBackGroundTexture
        //  2. Giá trị trả về: Không có
        public void SetTileBackGround(Texture2D _tileBackGroundTexture)
        {
            //Gán _tileArray bằng Texture2D _tileBackGroundTexture
            _tileArray = _tileBackGroundTexture;

            //Dựa vào kích thước _tileArray để gán số tile trong _tileArray vào biến _nTile
            _nTile = _tileArray.Width / _tileArray.Height;

        }

        //Hàm xử lí thao tác vẽ bản đồ lên màn hình game
        //  1. Tham số truyền vào:
        //      - SpriteBatch _spriteBatch
        //      - GameTime _gameTime
        //  2. Giá trị trả về: Không có
        public override void Draw(SpriteBatch _spriteBatch, GameTime _gameTime)
        {
            //Hướng xử lí trong hàm Draw
            //Kiểm tra: Nếu biến _isLoadMapSucceed mang giá trị true => tiếp tục xử lí
            //  Vẽ BackGround của bản đồ
            //  Thực hiện vẽ theo từng ô trên ma trận bản đồ
            //  Vẽ từ ô thứ ((_mapBegin.X / TileSize - 1) || 0) đến ô thứ ((_mapBegin.X / TileSize + WindowWidth / TileSize + 1) || 299)
            //  Vị trí bắt đầu vẽ tại điểm mapBegin trên màn hình game


            //Tiến hành
            //Kiểm tra: Nếu biến _isLoadMapSucceed mang giá trị true => tiếp tục xử lí
            if (_isLoadMapSucceed == false)
                return;

            _spriteBatch.Draw(_backGround, new Rectangle(0, 0, WindowWidth, WindowHeigh), Color.White);

            for (int collumn = mapBegin.X / TileSize - 1; collumn < _mapBegin.X / TileSize + WindowWidth / TileSize + 1 && collumn < MapWidth; collumn++)
            {
                for (int row = 0; row < 20; row++)
                {
                    if (collumn < 0)
                        collumn = 0;

                    //Kiểm tra mã số tile có nằm trong danh sách TileBackGround và mã số tile khác mã số kí hiệu vị trí bắt đầu của nhân vật
                    if ((mapMatrix[row][collumn] >= 0 && mapMatrix[row][collumn] < _nTile) && (mapMatrix[row][collumn] != nTile - 3))
                    {
                        //Gọi hàm vẽ của _spriteBatch cho từng ô trong mapMatrix, vẽ lên màn hình tile tương ứng với mã số trong mapMatrix
                        _spriteBatch.Draw(_tileArray,
                                          //new Rectangle(mapBegin.X + collumn * TileSize, mapBegin.Y + row * TileSize, TileSize, TileSize),
                                          new Rectangle(-mapBegin.X + collumn * TileSize, row * TileSize, TileSize, TileSize),
                                          new Rectangle((mapMatrix[row][collumn]) * _tileArray.Height, 0, _tileArray.Height, _tileArray.Height),
                                          Color.White);
                    }
                }
            }
        }
    }
}
